#include "preHeader.h"
#include "MapServer.h"
#include "Map/InstanceMgr.h"
#include "Service/MapServerService.h"
#include "Session/GateServerMgr.h"
#include "Session/GameServerMgr.h"
#include "Session/DBPServerMgr.h"
#include "Session/PacketDispatcher.h"

MapServer::MapServer()
: sWheelTimerMgr(1, GET_UNIX_TIME)
, m_pService(new MapServerService())
{
}

MapServer::~MapServer()
{
	delete m_pService;
}

WheelTimerMgr *MapServer::GetWheelTimerMgr()
{
	return &sWheelTimerMgr;
}

bool MapServer::Initialize()
{
	CreateTriggerX(ByDailyTrigger,
		MakeTriggerPoint(0,0,0), std::bind(&MapServer::OnNewDaily, this));
	CreateTriggerX(ByWeeklyTrigger,
		MakeTriggerPoint(1,0,0,0), std::bind(&MapServer::OnNewWeekly, this));
	CreateTriggerX(ByMonthlyTrigger,
		MakeTriggerPoint(1,0,0,0), std::bind(&MapServer::OnNewMonthly, this));
	return FrameWorker::Initialize();
}

void MapServer::Finish()
{
	sSessionManager.Stop();
	return FrameWorker::Finish();
}

void MapServer::Update(uint64 diffTime)
{
	AsyncTaskOwner::UpdateTask();
	sSessionManager.Update();
	m_pService->UpdateAllPackets();
}

void MapServer::OnTick()
{
	sGateServerMgr.RunGC();
	sSessionManager.Tick();
	sPacketDispatcher.Tick();
	sWheelTimerMgr.Update(GET_UNIX_TIME);
}

void MapServer::OnNewDaily()
{
	sInstanceMgr.ForeachAllPlayer(
		std::bind(&Player::OnNewDaily, std::placeholders::_1, false));
}

void MapServer::OnNewWeekly()
{
	sInstanceMgr.ForeachAllPlayer(
		std::bind(&Player::OnNewWeekly, std::placeholders::_1, false));
}

void MapServer::OnNewMonthly()
{
	sInstanceMgr.ForeachAllPlayer(
		std::bind(&Player::OnNewMonthly, std::placeholders::_1, false));
}

void MapServer::SendSingleMail(uint16 gsId, const inst_mail& mailProp)
{
	NetPacket pack(MS_MAIL);
	pack << (s32)MAIL_RQST_TYPE::SEND_SINGLE;
	SaveToINetStream(mailProp, pack);
	GSSession(gsId).PushSendPacket(pack);
}

void MapServer::SendMultiMail(const std::vector<ObjGUID>& receiverGuids,
	const inst_mail& mailProp)
{
	std::unordered_map<uint16, std::vector<uint32>> gsReceiverUids;
	for (auto receiverGuid : receiverGuids) {
		gsReceiverUids[receiverGuid.SID].push_back(receiverGuid.UID);
	}
	for (auto& pair : gsReceiverUids) {
		auto& curReceiverUids = pair.second;
		NetPacket pack(MS_MAIL);
		pack << (s32)MAIL_RQST_TYPE::SEND_MULTI_SAME << (u32)curReceiverUids.size();
		for (auto receiverUid : curReceiverUids) {
			pack << receiverUid;
		}
		SaveToINetStream(mailProp, pack);
		GSSession(pair.first).PushSendPacket(pack);
	}
}

void MapServer::SendMultiMail(const std::vector<uint16>& receiverGsIds,
	const std::vector<inst_mail>& mailProps)
{
	DBGASSERT(receiverGsIds.size() == mailProps.size());
	std::unordered_map<uint16, std::vector<size_t>> gsMailIndexs;
	for (size_t i = 0, n = receiverGsIds.size(); i < n; ++i) {
		gsMailIndexs[receiverGsIds[i]].push_back(i);
	}
	for (auto& pair : gsMailIndexs) {
		auto& curMailIndexs = pair.second;
		NetPacket pack(MS_MAIL);
		pack << (s32)MAIL_RQST_TYPE::SEND_MULTI_DIFFERENT << (u32)curMailIndexs.size();
		for (auto mailIndex : curMailIndexs) {
			SaveToINetStream(mailProps[mailIndex], pack);
		}
		GSSession(pair.first).PushSendPacket(pack);
	}
}

void MapServer::SendPacketToAllPlayer(const INetPacket& pck)
{
	sGateServerMgr.BroadcastPacket2AllClient(pck);
}

void MapServer::SendSysMsgToAll(ChannelType channelType, uint32 msgFlags,
	uint32 msgId, const std::string_view& msgArgs)
{
	NetPacket pack(SMSG_SYSTEM_MESSAGE);
	pack << (s8)channelType << msgFlags << msgId;
	pack.Append(msgArgs.data(), msgArgs.size());
	SendPacketToAllPlayer(pack);
}

void MapServer::KickPlayer(ObjGUID guid, GErrorCode error)
{
	NetPacket pack(MS_KICK);
	pack << GetGuid4Gs(guid) << (s32)error;
	GSSession(guid.SID).PushSendPacket(pack);
}

void MapServer::OfflinePlayer(size_t gsIdx)
{
	sInstanceMgr.ForeachAllInstance([=](MapInstance* pMapInstance) {
		pMapInstance->EventOfflinePlayer(gsIdx);
	});
}

void MapServer::OfflineAllPlayer()
{
	sInstanceMgr.ForeachAllInstance([=](MapInstance* pMapInstance) {
		pMapInstance->EventOfflinePlayer();
	});
	while (true) {
		OS::SleepS(1);
		for (auto pDBPSession : sDBPServerMgr.GetDBPServerList()) {
			if (pDBPSession->GetWaitReplyRequests() != 0) {
				continue;
			}
		}
		break;
	}
}
